<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.4"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Flow-IPC: Asynchronicity and Integrating with Your Event Loop</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr id="projectrow">
  <td id="projectalign">
   <div id="projectname">Flow-IPC<span id="projectnumber">&#160;2.0.0</span>
   </div>
   <div id="projectbrief">Flow-IPC project: Full implementation reference.</div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.9.4 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */
var searchBox = new SearchBox("searchBox", "search",'Search','.html');
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */
$(function() {
  initMenu('',true,false,'search.php','Search');
  $(document).ready(function() { init_search(); });
});
/* @license-end */
</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" 
        name="MSearchResults" id="MSearchResults">
</iframe>
</div>

</div><!-- top -->
<div><div class="header">
  <div class="headertitle"><div class="title">Asynchronicity and Integrating with Your Event Loop </div></div>
</div><!--header-->
<div class="contents">
<div class="textblock"><center><b>MANUAL NAVIGATION:</b> <a class="el" href="setup.html">Preceding Page</a> - <a class="el" href="session_setup.html">Next Page</a> - <a href="./pages.html"><b>Table of Contents</b></a> - <a class="el" href="namespaceipc.html"><b>Reference</b></a></center><hr  />
<p >Before using the most useful aspects of Flow-IPC, we must know how to integrate with an application's event loop. (Or go back to preceding page: <a class="el" href="setup.html">Prerequisites and Setup</a>.)</p>
<p >At its core Flow-IPC is meant to send messages to a process and receive them in that other process. As a rule, a given basic <em>send</em> API in Flow-IPC is synchronous, non-blocking, and can never return a "would-block" error. That is:</p><ul>
<li>It will return quickly.</li>
<li>Via return value and/or out-arg and/or lack/presence of thrown exception, it will report either total success; or failure indicating an unrecoverable error (meaning there is no point in trying the send operation later: the out-pipe at least is hosed).<ul>
<li>There is no such error as "would-block." That is, "buffers full; try send again later" is not a thing in our APIs. (If you're worried about performance implications of this &ndash; don't be. However this is not the page to discuss that.)</li>
</ul>
</li>
</ul>
<p >However, by their very nature, this is not the case for <em>receive</em> APIs (as well as spiritually similar operations like "accept"). By definition, at a given point in time, if I ask to receive something over an IPC channel, there may be no data available to receive at that moment, so the application must wait until <em>just</em> when the data have arrived and only then inform the part of the application interested in the received message. This page discusses how to accomplish this integration between a conceptual background receive operation (on the part of Flow-IPC) and the rest of your application.</p>
<p >The topic of event loops, asynchronicity, reactors, proactors, etc., in a C++ program is complex and expansive, and any comprehensive discussion thereof is <em>well</em> outside our scope. We do try to be helpful and skim the topic as needed for our purposes, but ultimately some experience and/or outside research on your part may be necessary for deep understanding.</p>
<h2>Your application's event loop versus an Flow-IPC async op </h2>
<p >Your application has a main thread used for logic that would make use of the results of IPC receives (and similar <b>async[hronous] op[eration]s</b>). It may use several such IPC-interested worker threads concurrently as well, in which case it is your responsibility to synchronize (guard against concurrency) between them. For simplicity of exposition we will exclusively discuss the one-thread scenario here; the same discussion can be generalized to multiple threads by the reader.</p>
<p >Let's call this thread, <b>thread U</b> (U for user). (Again &ndash; if you have multiple threads, presumably to make user of multiple processors cores, then the same conversation can be generalized to such threads U1, U2, ....)</p>
<p >Now consider a simple example Flow-IPC async-op (the same principles will apply to all others except where noted); we'll use it for further discussion of the topic. The async op: <a class="el" href="classipc_1_1transport_1_1Blob__receiver.html" title="A documentation-only concept defining the behavior of an object capable of reliably/in-order receivin...">ipc::transport::Blob_receiver</a> being asked to receive a single message, in the form of a blob, when it becomes available. While Flow-IPC waits for an incoming blob (conceptually in the <em>background</em>), thread U must be usable for other tasks (e.g., sending or receiving HTTP traffic; periodic logging and reporting; who knows); but once a blob does arrive:</p><ul>
<li>if thread U is sleeping, it must be woken up and given the resulting blob, which it will handle as it sees fit;</li>
<li>if it is doing something, then as soon as that work is done, it should be given the resulting blob, which it will handle as it sees fit.</li>
</ul>
<p >The <code>Blob_receiver</code> having to <em>conceptually</em> background-wait for incoming traffic and report the result to thread U, in <em>practice</em> can occur in one of two places:</p><ul>
<li>A separate worker thread (call it <b>thread W</b>): Flow-IPC may start thread W internally and have it sleep until the kernel, or some other mechanism, wakes it up and gives it some incoming data, which it would then potentially package into a complete message blob. Now thread W would signal thread U somehow, and give it the result-blob; thread U would handle it as soon as it is free (which might be immediately, if it's sleeping, or somewhat later if it's doing something else).</li>
<li>Thread U itself: Flow-IPC may <em>cooperate</em> with the coder of the application by sharing thread U (or multiple threads U1, U2, ...!) with its other tasks unknown to it. Thread U would sleep until the <em>earlier</em> of the following occurs:<ul>
<li>There are incoming data to the <code>Blob_receiver</code>.</li>
<li>Some other event of interest to thread U has occurred.</li>
</ul>
</li>
</ul>
<p >Which setup you will use is an important decision, especially in terms of how your code is structured overall. <em>Event loop integration</em> comprises this decision &ndash; and further decisions downstream of it.</p>
<p >Flow-IPC supports both ways of structuring your application. Moreover, there are different API variations depending on which one is chosen. We shall soon discuss specifics.</p>
<p >Furthermore, in the rest of the manual, for reasons of clarity and brevity, we have to mostly choose <em>one</em> event-loop-integration approach. We expect the reader will tailor their understanding to the other event-loop-integration approaches if and as needed. That said let's discuss these various approaches.</p>
<p ><a class="anchor" id="aio_pattern"></a></p><h2>Async-I/O pattern (default approach): Proactor pattern with separate Flow-IPC worker thread(s) </h2>
<p >This pattern is the one used in most of this Manual, except when specifically discussing event loop integration approach alternatives. I.e., this is the aforementioned <em>one event-loop-integration approach</em> referred-to just above.</p>
<p >Regular users of <a href="https://www.boost.org/doc/libs/1_82_0/doc/html/boost_asio.html">boost.asio</a> (on-track for the C++ standard), will be familiar with this pattern/approach. In it, thread U invokes a central <code>run()</code> method in which the entire event loop runs, until the decision is made to exit the loop (usually permanently). (<code>run()</code> can also be called in each of multiple threads U1, U2, ....) This method maintains an internal queue of <b>tasks</b> (basically, <code>void</code> functions); when the queue is empty it sleeps until a task is pushed onto it which it detects and immediately executes (and pops). Once that task exits, it executes any more tasks that may have been pushed onto the queue in the meantime &ndash; whether by 1+ of those tasks themselves, or other concurrent threads (spoiler alert: such as our thread W). Once there are no more tasks, it sleeps again.</p>
<dl class="section note"><dt>Note</dt><dd>Furthermore this sleep-until-tasks-queued-then-wake operation inside <code>run()</code> is integrated with OS-level events: If some part of the application wants to do something once TCP socket X, or Unix domain socket Y, receives data, then the kernel can be told to wake up the <code>run()</code>'s special "sleep" the moment 1+ of those sockets are readable. (Internally this is accomplished via <code>select()</code>, <code>poll()</code>, <code>epoll_wait()</code>, etc., which can be told to sleep until 1+ native handles have an active event.) Because of this there is not always a need to start a separate blocking thread W that would enqueue items onto the <code>run()</code> task-queue; instead the kernel has "special privileges" due to being integrated, at a low level, with the <code>poll()</code>/whatever OS-call.</dd></dl>
<p>That <b>note</b> aside: The most basic operation of interest to us has the following form:</p>
<div class="fragment"><div class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> Task&gt; <span class="comment">// Task = function type matching signature `void F()`.</span></div>
<div class="line"><span class="keywordtype">void</span> post(<a class="code hl_typedef" href="namespaceipc_1_1util.html#a31e67d3a017477a04b313927e2f2c733">Task</a>&amp;&amp; task);</div>
<div class="ttc" id="anamespaceipc_1_1util_html_a31e67d3a017477a04b313927e2f2c733"><div class="ttname"><a href="namespaceipc_1_1util.html#a31e67d3a017477a04b313927e2f2c733">ipc::util::Task</a></div><div class="ttdeci">flow::async::Task Task</div><div class="ttdoc">Short-hand for polymorphic function (a-la std::function&lt;&gt;) that takes no arguments and returns nothin...</div><div class="ttdef"><b>Definition:</b> <a href="util__fwd_8hpp_source.html#l00122">util_fwd.hpp:122</a></div></div>
</div><!-- fragment --><p >This operation, invokable both from within an already-executing task on thread U, or from any other thread, enqueues <code>std::move(task)</code> onto the task-queue, so that <code>run()</code> either wakes up thread U (if currently sleeping) and executes <code>task()</code> (no args), or else it'll get invoked once ongoing tasks have been executed and popped. <code>post()</code> itself does not synchronously invoke <code>task()</code>; but if invoked from within a task (i.e., from thread U, i.e., from <code>run()</code> itself), then <code>run()</code> will possibly invoke the moment the current (posting) task returns.</p>
<dl class="section note"><dt>Note</dt><dd><code>post()</code> is thread-safe, in that it takes care of any synchronization of the internally-kept task-queue, itself.</dd></dl>
<p>Now, consider the aforementioned <code>Blob_receiver</code> receive op. Its API is as follows:</p>
<div class="fragment"><div class="line"><span class="keyword">template</span>&lt;<span class="keyword">typename</span> Handler&gt; <span class="comment">// Handler = function type matching signature `void F(const Error_code&amp;, size_t n_sz)`.</span></div>
<div class="line"><span class="keywordtype">void</span> async_receive_blob(<a class="code hl_typedef" href="namespaceipc_1_1util.html#a6cb62ae434900f3a8915b33ec5d61a96">ipc::util::Blob_mutable</a> target_buf, Handler&amp;&amp; handler);</div>
<div class="ttc" id="anamespaceipc_1_1util_html_a6cb62ae434900f3a8915b33ec5d61a96"><div class="ttname"><a href="namespaceipc_1_1util.html#a6cb62ae434900f3a8915b33ec5d61a96">ipc::util::Blob_mutable</a></div><div class="ttdeci">boost::asio::mutable_buffer Blob_mutable</div><div class="ttdoc">Short-hand for an mutable blob somewhere in memory, stored as exactly a void* and a size_t.</div><div class="ttdef"><b>Definition:</b> <a href="util__fwd_8hpp_source.html#l00140">util_fwd.hpp:140</a></div></div>
</div><!-- fragment --><p >To use it, a task in the context of an event loop in thread U might be written as follows:</p>
<p ><a class="anchor" id="proactor_ex"></a> </p><div class="fragment"><div class="line"><span class="keyword">class </span>My_loop</div>
<div class="line">{</div>
<div class="line">  array&lt;uint8_t, 8192&gt; m_buf; <span class="comment">// This must be alive until handler() runs; so we choose to keep it as a data member.</span></div>
<div class="line">  Blob_receiver m_rcv;</div>
<div class="line"> </div>
<div class="line">  <span class="keywordtype">void</span> start_receive()</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// We are in thread U.</span></div>
<div class="line"> </div>
<div class="line">    m_rcv.async_receive_blob(<a class="code hl_typedef" href="namespaceipc_1_1util.html#a6cb62ae434900f3a8915b33ec5d61a96">ipc::util::Blob_mutable</a>(m_buf), <span class="comment">// Target buffer starting at m_buf.begin(), length .size() == 8192.</span></div>
<div class="line">                             [<span class="keyword">this</span>](<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code, <span class="keywordtype">size_t</span> n_sz)</div>
<div class="line">    {</div>
<div class="line">      <span class="comment">// We are in &quot;unspecified Flow-IPC thread&quot; (really, thread W)!  Must post() (unless shutting down anyway).</span></div>
<div class="line"> </div>
<div class="line">      <span class="keywordflow">if</span> (err_code == <a class="code hl_enumvalue" href="namespaceipc_1_1transport_1_1error.html#adfef15ef4e76cc55d652fbd9c2797297afe29343445a7bf167cc186a44bd2c6ed">ipc::transport::error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER</a>)</div>
<div class="line">      {</div>
<div class="line">        <span class="comment">// Stuff is shutting down; just get out.</span></div>
<div class="line">        <span class="keywordflow">return</span>;</div>
<div class="line">      }</div>
<div class="line">      <span class="comment">// else</span></div>
<div class="line"> </div>
<div class="line">      post([<span class="keyword">this</span>, err_code, n_sz]()</div>
<div class="line">      {</div>
<div class="line">        <span class="comment">// We are in thread U.</span></div>
<div class="line"> </div>
<div class="line">        <span class="keywordflow">if</span> (err_code)</div>
<div class="line">        {</div>
<div class="line">          <span class="comment">// Fatal error; m_rcv hosed.</span></div>
<div class="line"> </div>
<div class="line">          <span class="comment">// ...Handle it appropriately here....</span></div>
<div class="line"> </div>
<div class="line">          <span class="comment">// And usually that&#39;s it.</span></div>
<div class="line">          <span class="keywordflow">return</span>;</div>
<div class="line">        }</div>
<div class="line">        <span class="comment">// else: All good.  Result blob is at m_buf.begin(), is n_sz long.</span></div>
<div class="line">        on_receive(n_sz);</div>
<div class="line">      }</div>
<div class="line">    });</div>
<div class="line">  }</div>
<div class="line"> </div>
<div class="line">  <span class="keywordtype">void</span> on_receive(<span class="keywordtype">size_t</span> n_sz)</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// We are in thread U.</span></div>
<div class="line"> </div>
<div class="line">    <span class="comment">// ...Handle the received blob at m_buf.begin() of size n_sz....</span></div>
<div class="line"> </div>
<div class="line">    <span class="comment">// Perhaps async-receive the next one:</span></div>
<div class="line">    start_receive();</div>
<div class="line">  }</div>
<div class="line">};</div>
<div class="ttc" id="anamespaceipc_1_1transport_1_1error_html_adfef15ef4e76cc55d652fbd9c2797297afe29343445a7bf167cc186a44bd2c6ed"><div class="ttname"><a href="namespaceipc_1_1transport_1_1error.html#adfef15ef4e76cc55d652fbd9c2797297afe29343445a7bf167cc186a44bd2c6ed">ipc::transport::error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER</a></div><div class="ttdeci">@ S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER</div><div class="ttdoc">Async completion handler is being called prematurely, because underlying object is shutting down,...</div></div>
<div class="ttc" id="anamespaceipc_html_aa3192e586cc45d3e7c22463bf2760f89"><div class="ttname"><a href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">ipc::Error_code</a></div><div class="ttdeci">flow::Error_code Error_code</div><div class="ttdoc">Short-hand for flow::Error_code which is very common.</div><div class="ttdef"><b>Definition:</b> <a href="common_8hpp_source.html#l00298">common.hpp:298</a></div></div>
</div><!-- fragment --><p >The key points:</p><ul>
<li>When operating according to this pattern, the user handler is always invoked from (formally) "unspecified thread that is not thread U." In reality it is thread W in our case, but that should not matter.</li>
<li>In the proactor pattern &ndash; when there's only one thread U as opposed to concurrent workers U1, U2, ... &ndash; user tasks can safely assume they're never invoked concurrently with each other. Therefore, usually, the completion handler supplied to this Flow-IPC API (<code>Blob_receiver::async_receive_blob()</code> in this case) should perform the bulk of its work in thread U and thus must invoke <code>post()</code>, capturing any required args in the lambda first.<ul>
<li>The only exception: The special-meaning <code>Error_code S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER</code> (spiritually equivalent to boost.asio's <code>operation_aborted</code>) indicates the async-op is of the <b>one-off</b> variety, where the completion handler is <em>guaranteed</em> to be invoked <em>exactly</em> once; and in particular if the source object is destroyed before the operation completes in the background, then it still invokes the completion handler but reports operation-aborted. This means the user should, usually, just no-op, as stuff (including quite possibly their own <code>*this</code>) is probably in the middle of destruction. In that case one might as well immediately <code>return;</code> rather than <code>post()</code> a thing that would basically do so anyway.</li>
</ul>
</li>
<li>If the async-op targets some kind of user resource (in this case a simple buffer in memory), then that resource must be available until the completion handler is invoked. Otherwise undefined behavior results.<ul>
<li>Other techniques beyond maintaining a long-lived data member (like <code>m_buf</code> above) exist. In particular it's common to allocate and lambda-capture a <code>shared_ptr</code> to the underlying resource.</li>
</ul>
</li>
</ul>
<p >To reiterate: In most of the rest of the Manual, we use the above pattern. <b>In particular we assume the existence of a <code>post()</code> method as shown above</b> and <em>use it regularly in example code</em>. Note that the exact API your application uses will be spiritually identical to <code>post(F)</code> (where <code>F()</code> is the task) but specifically different. In particular/for example:</p><ul>
<li>If you are using boost.asio, it'll be <code>post(E, F)</code>, where <code>E</code> is usually a <code>boost::asio::io_context</code> (or <code>strand</code>).</li>
<li>If you are using <code>flow::async</code> (a boost.asio-helper), it'll be <code>L.post(F)</code>, where <code>L</code> is <code>Single_thread_task_loop</code> (1 user worker thread) or <code>Concurrent_task_loop</code> (2+ threads).<ul>
<li>Or a lower-level utility is <code>asio_exec_ctx(..., E, ..., F)</code>, which is a variant of boost.asio's <code>post(E, F)</code> with certain added options.</li>
</ul>
</li>
</ul>
<p >This pattern is referred to as <b>async-I/O pattern</b>.</p>
<h3>I'm a boost.asio user. Why do I have to <code>post()</code>? Can't I just give your constructor my <code>io_context</code>, and then you'll place the completion handler directly onto it?</h3>
<p >In other words, why didn't we just make <code>Blob_receiver</code> (among others) a boost.asio I/O-object, like <code>boost::asio::ip::tcp::socket</code>?</p>
<p >We feel you. We use boost.asio ourselves internally after all! However the first priority was to supply a more general API, so that one does not <em>need</em> to orient their event loop around boost.asio machinery specifically &ndash; but merely have a task-queue and <code>post()</code>-like API available. That said:</p>
<dl class="todo"><dt><b><a class="el" href="todo.html#_todo000042">Todo:</a></b></dt><dd>We may supply an alternative API wherein Flow-IPC objects can be boost.asio I/O objects themselves, similarly to <code>boost::asio::ip::tcp::socket</code>.</dd></dl>
<p ><a class="anchor" id="sio_pattern_conceptual"></a></p><h2>Alternate approaches: sharing thread U; reactor pattern </h2>
<p >Let's set aside, for the moment, the stylistic organization of the event loop. Purely in terms of performance, is there any problem with the approach outlined above, where thread(s) like thread W is/are launched by Flow-IPC in the background and then signal thread(s) like thread U when background events occur?</p>
<p >Yes and no. For most applications the only perf cost is that of (1) context switching between user threads U1, U2, ... versus Flow-IPC internal threads W1, W2, ....; and (2) the signaling necessary for a thread W to inform thread U to wake up. (2) is, essentially, a condition-variable ping and is rather cheap. (1) has a non-zero cost but for most applications, with modern hardware, is usually taken to be negligible.</p>
<p >However that is not an absolute truth. Some applications are extremely latency-sensitive and don't want to add even single microseconds of delay, if they can be avoided. Hence there is a non-zero market for, after all, having Flow-IPC and the application share thread U (or U1, U2, ...) &ndash; based on fine performance needs.</p>
<p >Furthermore some (most?) application event loops are not written in the proactor style but rather reactor style; that is they may not feature a <code>post()</code>-like API, or a task-queue, at all. Instead such an application, especially an old-school networking server, might manually use a <code>select()</code>/<code>poll()</code>/<code>epoll_wait()</code> in thread U, which sleeps until the kernel wakes it up due to a socket event, or a timeout expires (usually set to the remaining time on the next-soonest-to-fire timer event). Having woken up it would handle all active events and/or the applicable timer(s) and go back into the <code>poll()</code>/whatever.</p>
<p >It is possible and reasonable to modify such an event loop to integrate with the <em>async-I/O-pattern</em> API (that starts background worker threads W as needed) explained above, and some event-loop libraries already have that capability. It involves, basically, keeping a mutex-protected task (function pointer/function object) queue, plus a trivial IPC handle &ndash; e.g., to an unnamed pipe &ndash; which participates in the central <code>poll()</code>/whatever. In the thread W-invoked handler passed-to (e.g.) <code>Blob_receiver::async_receive_blob()</code>, one would invoke some API that locks the mutex, pushes the "true" handler (that the user wants to run in thread U) onto the task-queue, unlocks the mutex, and sends a byte to the aforementioned pipe. This would wake (if needed) the <code>poll()</code>/whatever, code subsequent to whose return would then synchronously invoke all the tasks in the task queue (locking the mutex of course).</p>
<p >However, in that case, the <em>possible</em> performance deficit due to the switching between threads U and W and the required signaling may still be a concern for some applications.</p>
<p >Let's discuss what one might do to share thread U instead while using Flow-IPC.</p>
<p ><a class="anchor" id="shared_proactor"></a></p><h3>Proactor pattern while sharing thread U</h3>
<p >Suppose we wanted to use Flow-IPC in a way as closely resembling the proactor approach outlined above (<code>Blob_receiver::async_receive_blob()</code> for example) as possible &ndash; but keep the work in thread U; that is avoiding the <code>post()</code>-onto-thread-U step in the handler passed-to <code>async_receive_blob()</code>. This might be the best of both worlds: fewer threads/context switching; code that is similar to the above already-reasonable-simple pattern &ndash; but even simpler still. Sounds good!</p>
<p >The best and de-facto-standard C++ proactor event-loop API is boost.asio. The authors are not aware of any better or more general way to discuss proactor-style event loops. (<code>flow::async</code> makes it easier to work with boost.asio, but it is still in essence working with boost.asio.) Therefore the problem of using proactor pattern, but with thread U alone, reduces to using boost.asio but with thread U alone.</p>
<p ><a class="anchor" id="shared_reactor"></a></p><h3>Reactor (<code>poll()</code>-y) pattern while sharing thread U</h3>
<p >Lastly, as noted before, the application might be oriented around an old-school &ndash; but nevertheless proven and effective &ndash; reactor-pattern event loop summarized above. Does Flow-IPC have a way of integrating with such an event loop <em>without</em> starting unnecessary threads W (i.e., sharing thread U with the application)?</p>
<p >Such an API shall be oriented around native handles; i.e., it will provide certain internal handles (FDs in Linux and friends) and expect the application to wait for activity on those handles, among others of interest to the application, and to invoke Flow-IPC processing to then handle the relevant events and produce relevant results &ndash; such as "a message blob has been received as the user has requested; here it is" &ndash; directly in thread U. Or &ndash; for that matter &ndash; from multiple (of your) threads U1, U2, ....</p>
<h3>Okay, so then... how?</h3>
<p >Both alternate approaches described above are possible by using the following Flow-IPC alternate (to the async-I/O pattern) API: the <code>sync_io</code> <b>pattern</b>.</p>
<h2><code>sync_io</code> pattern (alternate approach) </h2>
<p >For every conceptual object type in Flow-IPC that conceptually performs asynchronous (long-running, blocking, not-non-blocking, annoying &ndash; take your pick) operations, there are indeed 2 mutually exclusive APIs available. So for each conceptual "thing," there are 2 similarly named classes or class templates. They are:</p><ul>
<li>The (<a class="el" href="async_loop.html#aio_pattern">recommended by default</a>) async-I/O-pattern API. Let's say it's called <code>"N::X"</code>, where <code>N</code> is a namespace, while <code>X</code> is a class or class template (or alias to it).<ul>
<li>Again: this is the one we use in almost all discussion and examples in this Manual elsewhere.</li>
</ul>
</li>
<li>The (<a class="el" href="async_loop.html#sio_pattern_conceptual">alternate, advanced</a>) <code>sync_io</code>-pattern API. It shall typically be called <code>"N::sync_io::X"</code>. (At any rate, it shall <em>always</em> live in a sub-namespace named <code>sync_io</code>.)</li>
</ul>
<p >In actual fact, for perf-sensitive <code>X</code>es, usually the <code>sync_io::X</code> is a lighter-weight <em>core</em> with the nitty-gritty logic of dealing with whatever it is that an <code>X</code> represents (Unix domain socket, MQ, channel...). Then "plain" <code>X</code> &ndash; the async-I/O variety &ndash; is built <em>around</em> such a <code>sync_io</code> core. In these cases, an <code>X</code> can <em>always</em> be constructed from an <code>std::move(sync_io::X)</code> at low computational cost. This might be counterintuitive at first, but it actually makes sense:</p><ul>
<li>The <code>sync_io</code> core allows for great flexibility on the user's part, but is (all else being equal) harder to use.</li>
<li>The async-I/O adapter of that core adds more "stuff" to make it work on top of that core; but it is easier for the user to use.<ul>
<li>In fact such an async-I/O adapter is eating our own dog food: it is always a use case of how a <code>sync_io::X</code> can be effectively leveraged in a 2+-thread situation (user thread U, worker thread W). <em>If</em> this use case is itself not sufficient for <em>your</em> application, then you can use the core directly yourself.</li>
</ul>
</li>
</ul>
<p >So we'd like you to use the async-I/O <code>X</code> when possible, otherwise <code>sync_io::X</code> when that is insufficient.</p>
<p >How does a <code>sync_io</code>-pattern API work? We won't restate it here, or indeed (as of this writing) feature a Manual page for it. It is an advanced topic and is fully covered in the reference documentation. Namely find it here: <a class="el" href="namespaceipc_1_1util_1_1sync__io.html" title="Contains common code, as well as important explanatory documentation in the following text,...">ipc::util::sync_io</a> doc header. Loosely speaking it supplies an API analogous to OpenSSL's <code>SSL_read()</code> sometimes issuing <code>SSL_ERROR_WANT_READ</code> or <code>SSL_ERROR_WANT_WRITE</code>.</p>
<p >That said a quick note w/r/t to how one might use the <a class="el" href="namespaceipc_1_1util_1_1sync__io.html" title="Contains common code, as well as important explanatory documentation in the following text,...">ipc::util::sync_io</a> APIs to implement the (1) "sharing thread U" and (2) "reactor pattern" possibilities mentioned above:</p><ul>
<li><a class="el" href="async_loop.html#shared_proactor">Sharing thread U</a>: Use the "Integrating with boost.asio" technique discussed in the <a class="el" href="namespaceipc_1_1util_1_1sync__io.html#aa0b9a3cc6bdc7dedbef4f9e06851aa24" title="In sync_io pattern, concrete type storing user-supplied function invoked by pattern-implementing ipc:...">ipc::util::sync_io::Event_wait_func</a> doc header.</li>
<li><a class="el" href="async_loop.html#shared_reactor">Reactor pattern</a>: Use the "Integrating with reactor-pattern `poll()` and similar" technique discussed in the same doc header.</li>
</ul>
<p >Here is a list of async-I/O-pattern APIs and their <code>sync_io</code> counterparts.</p>
<ul>
<li><a class="el" href="namespaceipc_1_1transport.html" title="Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...">ipc::transport</a> core layer:<ul>
<li>Concepts: <a class="el" href="classipc_1_1transport_1_1Blob__sender.html" title="A documentation-only concept defining the behavior of an object capable of reliably/in-order sending ...">ipc::transport::Blob_sender</a>, <a class="el" href="classipc_1_1transport_1_1Native__handle__sender.html" title="A documentation-only concept defining the behavior of an object capable of reliably/in-order sending ...">ipc::transport::Native_handle_sender</a>, <a class="el" href="classipc_1_1transport_1_1Blob__receiver.html" title="A documentation-only concept defining the behavior of an object capable of reliably/in-order receivin...">ipc::transport::Blob_receiver</a>, <a class="el" href="classipc_1_1transport_1_1Native__handle__receiver.html" title="A documentation-only concept defining the behavior of an object capable of reliably/in-order receivin...">ipc::transport::Native_handle_receiver</a>.<ul>
<li>(<code>sync_io</code>-pattern) <a class="el" href="classipc_1_1transport_1_1sync__io_1_1Blob__sender.html" title="A documentation-only concept: what transport::Blob_sender is to transport::Native_handle_sender (name...">ipc::transport::sync_io::Blob_sender</a>, <a class="el" href="classipc_1_1transport_1_1sync__io_1_1Native__handle__sender.html" title="A documentation-only concept defining the behavior of an object that is the sync_io-pattern counterpa...">ipc::transport::sync_io::Native_handle_sender</a>, <a class="el" href="classipc_1_1transport_1_1sync__io_1_1Blob__receiver.html" title="A documentation-only concept: what transport::Blob_receiver is to transport::Native_handle_receiver (...">ipc::transport::sync_io::Blob_receiver</a>, <a class="el" href="classipc_1_1transport_1_1sync__io_1_1Native__handle__receiver.html" title="A documentation-only concept defining the behavior of an object that is the sync_io-pattern counterpa...">ipc::transport::sync_io::Native_handle_receiver</a>.</li>
<li>Impls: <a class="el" href="classipc_1_1transport_1_1Native__socket__stream.html" title="Implements both Native_handle_sender and Native_handle_receiver concepts by using a stream-oriented U...">ipc::transport::Native_socket_stream</a> (+), <a class="el" href="classipc_1_1transport_1_1Blob__stream__mq__sender.html" title="Implements Blob_sender concept by using an adopted Persistent_mq_handle MQ handle to an MQ (message q...">ipc::transport::Blob_stream_mq_sender</a>, <a class="el" href="classipc_1_1transport_1_1Blob__stream__mq__receiver.html" title="Implements Blob_receiver concept by using an adopted Persistent_mq_handle MQ handle to an MQ (message...">ipc::transport::Blob_stream_mq_receiver</a>, <a class="el" href="classipc_1_1transport_1_1Channel.html" title="Peer to a bundle of 1-2 full-duplex pipe(s), one for transmitting unstructured binary blobs; the othe...">ipc::transport::Channel</a>.<ul>
<li>(<code>sync_io</code>-pattern) <a class="el" href="classipc_1_1transport_1_1sync__io_1_1Native__socket__stream.html" title="Implements both sync_io::Native_handle_sender and sync_io::Native_handle_receiver concepts by using a...">ipc::transport::sync_io::Native_socket_stream</a>, <a class="el" href="classipc_1_1transport_1_1sync__io_1_1Blob__stream__mq__sender.html" title="Implements sync_io::Blob_sender concept by using an adopted Persistent_mq_handle MQ handle to an MQ (...">ipc::transport::sync_io::Blob_stream_mq_sender</a>, <a class="el" href="classipc_1_1transport_1_1sync__io_1_1Blob__stream__mq__receiver.html" title="Implements sync_io::Blob_receiver concept by using an adopted Persistent_mq_handle MQ handle to an MQ...">ipc::transport::sync_io::Blob_stream_mq_receiver</a>, <a class="el" href="classipc_1_1transport_1_1Channel.html" title="Peer to a bundle of 1-2 full-duplex pipe(s), one for transmitting unstructured binary blobs; the othe...">ipc::transport::Channel</a> (#).</li>
</ul>
</li>
</ul>
</li>
<li>Other: <a class="el" href="classipc_1_1transport_1_1Native__socket__stream__acceptor.html" title="A server object that binds to a Shared_name and listens for incoming Native_socket_stream connect att...">ipc::transport::Native_socket_stream_acceptor</a> (**).<ul>
<li>(<code>sync_io</code>-pattern) <a class="el" href="classipc_1_1transport_1_1sync__io_1_1Native__socket__stream__acceptor.html" title="sync_io-pattern counterpart to async-I/O-pattern transport::Native_socket_stream_acceptor.">ipc::transport::sync_io::Native_socket_stream_acceptor</a>.</li>
</ul>
</li>
</ul>
</li>
<li><a class="el" href="namespaceipc_1_1transport.html" title="Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...">ipc::transport</a> structured layer:<ul>
<li>Main guy: <a class="el" href="classipc_1_1transport_1_1struc_1_1Channel.html" title="Owning and wrapping a pre-connected transport::Channel peer (an endpoint of an established channel ov...">ipc::transport::struc::Channel</a>.<ul>
<li>(<code>sync_io</code>-pattern) <a class="el" href="classipc_1_1transport_1_1struc_1_1sync__io_1_1Channel.html" title="sync_io-pattern counterpart to async-I/O-pattern transport::struc::Channel.">ipc::transport::struc::sync_io::Channel</a>.</li>
</ul>
</li>
</ul>
</li>
<li><a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a>:<ul>
<li>Concepts: <a class="el" href="classipc_1_1session_1_1Session.html" title="A documentation-only concept defining the local side of an IPC conversation (session) with another en...">ipc::session::Session</a>.<ul>
<li>Impls: <a class="el" href="namespaceipc_1_1session.html#a77d8fda00e1e17f595a6a5d4c44375e7" title="A vanilla Client_session with no optional capabilities.">ipc::session::Client_session</a>, <a class="el" href="namespaceipc_1_1session.html#ab01d5bce0ab2f3435ca9dd88d27609dc" title="A vanilla Server_session with no optional capabilities.">ipc::session::Server_session</a>, <a class="el" href="namespaceipc_1_1session_1_1shm_1_1classic.html#ac6c443a72e0f22710c83bb1e8c229618" title="This is to session::Client_session what shm::classic::Server_session is to session::Server_session.">ipc::session::shm::classic::Client_session</a>, <a class="el" href="classipc_1_1session_1_1shm_1_1classic_1_1Server__session.html" title="Identical to session::Server_session in every way, except that it makes available two SHM arenas,...">ipc::session::shm::classic::Server_session</a>, <a class="el" href="namespaceipc_1_1session_1_1shm_1_1arena__lend_1_1jemalloc.html#a837b4566cb2a734eb0ba58f285d084fe" title="This is to session::Client_session what shm::arena_lend::jemalloc::Server_session is to session::Serv...">ipc::session::shm::arena_lend::jemalloc::Client_session</a>, <a class="el" href="classipc_1_1session_1_1shm_1_1arena__lend_1_1jemalloc_1_1Server__session.html" title="Identical to session::Server_session in every way, except that it makes available two SHM arenas,...">ipc::session::shm::arena_lend::jemalloc::Server_session</a>.<ul>
<li>(<code>sync_io</code>-pattern) <a class="el" href="classipc_1_1session_1_1sync__io_1_1Client__session__adapter.html" title="sync_io-pattern counterpart to async-I/O-pattern session::Client_session types and all their SHM-awar...">ipc::session::sync_io::Client_session_adapter</a> (*), <a class="el" href="classipc_1_1session_1_1sync__io_1_1Server__session__adapter.html" title="sync_io-pattern counterpart to async-I/O-pattern session::Server_session types and all their SHM-awar...">ipc::session::sync_io::Server_session_adapter</a> (*).</li>
</ul>
</li>
</ul>
</li>
<li>Other: <a class="el" href="classipc_1_1session_1_1Session__server.html" title="To be instantiated typically once in a given process, an object of this type asynchronously listens f...">ipc::session::Session_server</a>, <a class="el" href="classipc_1_1session_1_1shm_1_1classic_1_1Session__server.html" title="This is to vanilla Session_server what shm::classic::Server_session is to vanilla Server_session: it ...">ipc::session::shm::classic::Session_server</a>, <a class="el" href="classipc_1_1session_1_1shm_1_1arena__lend_1_1jemalloc_1_1Session__server.html" title="This is to vanilla Session_server what shm::arena_lend::jemalloc::Server_session is to vanilla Server...">ipc::session::shm::arena_lend::jemalloc::Session_server</a>.<ul>
<li>(<code>sync_io</code>-pattern) <a class="el" href="classipc_1_1session_1_1sync__io_1_1Session__server__adapter.html" title="sync_io-pattern counterpart to async-I/O-pattern session::Session_server types and all their SHM-awar...">ipc::session::sync_io::Session_server_adapter</a> (*).</li>
</ul>
</li>
</ul>
</li>
</ul>
<p >In all cases, with the following exceptions, the object type is considered perf-sensitive, and therefore is indeed arranged as follows: <code>sync_io::X</code> core; <code>X</code> adapts such a core and be constructed from one cheaply.</p>
<p >The exceptions:</p><ul>
<li>(#) <a class="el" href="classipc_1_1transport_1_1Channel.html" title="Peer to a bundle of 1-2 full-duplex pipe(s), one for transmitting unstructured binary blobs; the othe...">ipc::transport::Channel</a> is a <em>bundling</em> template, essentially combining <code>..._sender</code> and <code>..._receiver</code> peer-objects in one object. It bundles either <code>X</code>es <em>or</em> <code>sync_io::X</code>es, but the same template handles both duties. It contains little logic: mainly just bundling. Hence it can pull it off without issue. Hence there's no <code>sync_io::Channel</code>.<ul>
<li>An async-I/O-bearing <code>Channel</code> can be trivially created from a <code>sync_io</code>-bearing one: <code>auto async_ch = sync_ch.async_io_obj()</code>.</li>
</ul>
</li>
<li>(**) Not being perf-sensitive, internally the <code>sync_io::Native_socket_stream_acceptor</code> is actually built around an async-I/O <code>Native_socket_stream_acceptor</code>. Makes no difference to the user: the APIs work regardless of how it is implemented internally. That said one cannot construct one from the other or vice versa.</li>
<li>(*) Not being perf-sensitive, all the <code>Session</code> and <code>Session_server</code> types are written primarily in async-I/O fashion. However, for those wishing to more easily integrate a <code>poll()</code>-y event loop with session-connect (client) and session-accept (server) operations, a <code>sync_io</code> API is available. Use <code>Client_session_adapter</code> to adapt <em>any</em> <code>Client_session</code>-like type (namely the templates listed there) &ndash; simply supply the latter type as the template arg. Ditto <code>Server_session_adapter</code> for <em>any</em> <code>Server_session</code>-like type. Lastly <code>Session_server_adapter</code> adapts <em>any</em> (of the 3) <code>Session_server</code>-like types.</li>
</ul>
<p >Lastly: for generic programming each relevant type features a couple of alias members:</p><ul>
<li><code>Sync_io_obj</code>: In an <code>X</code>: this is <code>sync_io::X</code>. In a <code>sync_io::X</code>: this is empty type <code>Null_peer</code>.</li>
<li><code>Async_io_obj</code>: Conversely in a <code>sync_io::X</code>: this is <code>X</code>. In an <code>X</code>: this is empty type <code>Null_peer</code>.</li>
</ul>
<p >E.g., <code>Blob_stream_mq_sender&lt;Posix_mq_handle&gt;::Sync_obj</code> is <code>sync_io::Blob_stream_mq_sender&lt;Posix_mq_handle&gt;</code> and conversely via <code>"::Async_obj"</code>.</p>
<hr  />
<dl class="section user"><dt>When one object generates/subsumes another object</dt><dd>As of this writing these are such cases:<ul>
<li><code><a class="el" href="namespaceipc_1_1transport_1_1struc_1_1shm_1_1classic.html#ac543b500a85f556564e592cbbefb6185" title="Convenience alias: Use this when constructing a struc::Channel with tag Channel_base::S_SERIALIZE_VIA...">struc::Channel::Channel()</a></code>: The <code>struc::Channel</code> subsumes an unstructured, pre-opened <a class="el" href="classipc_1_1transport_1_1Channel.html" title="Peer to a bundle of 1-2 full-duplex pipe(s), one for transmitting unstructured binary blobs; the othe...">ipc::transport::Channel</a> via <code>move()</code> semantics.</li>
<li><code>Native_socket_stream_acceptor::async_accept()</code>: The acceptor generates <code>Native_socket_stream</code>s.</li>
<li><code>Session::open_channel()</code> + <code>Session</code> passive-opening + <code>Session</code> init-channels: <code>Session</code> generates <code>Channel</code>s.</li>
<li><code>Session_server::async_accept()</code>: <code>Session_server</code> generates <code>Server_session</code>s.</li>
</ul>
</dd></dl>
<dl class="section user"><dt></dt><dd>In the latter case: the generator guy (whether vanilla/async-I/O <code>Session_server</code> or <code>sync_io::Session_server_adapter&lt;Session_server&gt;</code>) generates an object of the same API-type. I.e., async-I/O guy generates async-I/O guys; <code>sync_io</code> guy generates <code>sync_io</code> guys. Rationale: These are not perf-critical, and we reasoned that if one went to the trouble of wanting to use a <code>sync_io</code> server, then they'll want it to generate <code>sync_io</code> sessions to integrate into the same, or similarly built, event loop.</dd></dl>
<dl class="section user"><dt></dt><dd>In the former three cases: the generator guy (whether async-I/O-pattern or not!) generates <code>sync_io</code> cores. I.e., acceptor fills-out a <code>sync_io</code> socket-stream; open-channel method/passive-open handler/session-async-accept/session-sync-connect each fills-out a <code>sync_io</code> channel object. The subsumer guy (whether async-I/O-pattern or not!) subsumes a <code>sync_io</code> core. I.e., a structured-channel ctor takes over a <code>sync_io</code> channel (which may have in fact been generated by <code>Session::open_channel()</code>... synergy!). Rationale: <code>sync_io</code> cores are lighter-weight to generate and pass around, and one can always very easily then "upgrade" them to async-I/O objects as needed:<ul>
<li>Use <a class="el" href="classipc_1_1transport_1_1Native__socket__stream.html" title="Implements both Native_handle_sender and Native_handle_receiver concepts by using a stream-oriented U...">ipc::transport::Native_socket_stream</a> ctor that takes a <code>sync_io::Native_socket_stream&amp;&amp;</code>.</li>
<li>Use <a class="el" href="classipc_1_1transport_1_1Channel.html#add016cbac936d3da6c861755ac88b7a2" title="Converts a sync_io-peer-bearing *this to a returned async-I/O-peer-bearing new Channel,...">ipc::transport::Channel::async_io_obj()</a>.</li>
</ul>
</dd></dl>
<hr  />
<h2>Recap </h2>
<p >The simplest event-loop integration approach is the proactor-pattern-with-Flow-IPC-internally-starting-threads-as-needed: the <b>async-I/O pattern</b>. How to use this API style is explained <a class="el" href="async_loop.html#proactor_ex">here</a>. Most of the rest of the Manual uses this approach in examples and discussion. In particular we'll routinely use <code>post()</code> as described there.</p>
<p >We do this (1) for simplicity of exposition; and (2) because, in the absence of compelling objections, we feel it is the best approach to use. However such objections may, indeed, sometimes apply; namely in the presence of (1) a legacy reactor loop; and/or (2) very stringent perf restrictions sensitive even to context switching. In that case, whether merging Flow-IPC worker thread(s) into your own or integrating with a <code>*poll()</code>-y loop, use the alternate <code>sync_io</code> <b>pattern</b>, per: <a class="el" href="namespaceipc_1_1util_1_1sync__io.html" title="Contains common code, as well as important explanatory documentation in the following text,...">ipc::util::sync_io</a> doc header.</p>
<p >We're now ready to make Flow-IPC actually do something. Let's move on to <a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a>.</p>
<hr  />
<center><b>MANUAL NAVIGATION:</b> <a class="el" href="setup.html">Preceding Page</a> - <a class="el" href="session_setup.html">Next Page</a> - <a href="./pages.html"><b>Table of Contents</b></a> - <a class="el" href="namespaceipc.html"><b>Reference</b></a></center> </div></div><!-- contents -->
</div><!-- PageDoc -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated on Fri Apr 11 2025 20:02:26 for Flow-IPC by&#160;<a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.9.4
</small></address>
</body>
</html>
